using System;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Security;
using MCMLXVII.Core.Common;
using MCMLXVII.BackEnd.Services.SessionManagement.Messages;
using MCMLXVII.BackEnd.Data.SystemDatabase;
using MCMLXVII.BackEnd.Data.SystemDatabase.Parameters;
using MCMLXVII.BackEnd.Pipeline;
using MCMLXVII.SubSystems.Security.DirectoryServices;
using MCMLXVII.Core.Common.BackEnd;
using MCMLXVII.BackEnd.Data.SystemDatabase.DataSets;
using System.DirectoryServices;
using MCMLXVII.BackEnd.Common;
using System.IdentityModel.Tokens;
using System.ServiceModel;
using System.Security.Principal;
using MCMLXVII.BackEnd.Services.Common.Messages;

namespace MCMLXVII.BackEnd.Services.SessionManagement.BusinessActions
{
	/// <summary>
	/// This class implements the logon business logic
	/// </summary>  
	public static class NewSession
	{

        public static msgNewSessionResponse GetNewSession(msgNewSessionRequest Request)
		{
            string Domain = "";
            string LoginName = "";

            OperationContext context = OperationContext.Current;
            if (!context.ServiceSecurityContext.IsAnonymous  && context.ServiceSecurityContext.PrimaryIdentity.Name == "")
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Functional, Properties.Resources.WrongSecurityToken ));


            if (!AuthenticateIdentity(ref Domain, ref LoginName))
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Functional, Properties.Resources.WrongSecurityToken ));
            
            DSSecurityProviders.SecurityProvidersAndClassRow Inf = Security.GetDomainInfo(Domain);
            if (Inf == null)
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Functional, Properties.Resources.UnableToLogon));

            //Locate login Member
            DSMembers.OrganizationMembersAndClassDataTable dt = Security.GetMembersInfo(Inf.SecProviderID, LoginName);
            if (dt == null || dt.Rows.Count != 1)
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Functional, Properties.Resources.InvalidCredentials));


            //Validate access to core tool actions
            DSMembers.OrganizationMembersAndClassRow Member = (DSMembers.OrganizationMembersAndClassRow)dt.Rows[0];
            if (!(Security.CheckPermissionForTool(Member.MemberID, Request.Tool)==Security.AccessLevel.Allowed))
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Security, Properties.Resources.AccessDenied));

            // Create the Session Descriptor
            string[] SecurityDescriptor = Security.GetSecurityDescriptor(Member.MemberID, Request.Tool);
            StringBuilder sb = new StringBuilder();
            sb.Append("<Permissions>");
            for (int i = 0; i < SecurityDescriptor.Length; i++)
            {
                sb.Append("<Permission Name=\""+SecurityDescriptor[i]+"\"/>");
            }
            sb.Append("</Permissions>");
            // BETA: System.Data.SqlTypes.SqlXml sqx = new System.Data.SqlTypes.SqlXml(new XmlTextReader(sb.ToString(), XmlNodeType.Document, null));
             Security.CreateNewSession(Member.MemberID, sb.ToString() );           
            return new msgNewSessionResponse("OK", ParametersCache.GetVarchar("SystemVersion"), DateTime.Now.AddHours(ParametersCache.GetInt("SessionLeaseTime")), Guid.Empty , Member.Description, SecurityDescriptor, "", Member.Language, Member.MemberID);
        }


        private static bool AuthenticateIdentity(ref string Domain, ref string LoginName)
        {
            CallContext Context = null;
            OperationContext context = OperationContext.Current;
            
            switch (context.ServiceSecurityContext.PrimaryIdentity.AuthenticationType)
            {
                case "Windows":
                    if (context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated)
                    {
                        if (context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") > 0)
                        {
                            Domain = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(0, context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\"));
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") + 1, context.ServiceSecurityContext.WindowsIdentity.Name.Length - context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") - 1);
                        }
                        else
                        {
                            Domain = ParametersCache.GetVarchar(Constants.PNDefaultDomainNBTName);
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Trim();
                        }
                    }
                    return context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated;
                case "UserName":
                    UserNameSecurityToken us = (UserNameSecurityToken)Context.SecurityToken;
                    if (us.UserName.LastIndexOf("\\") > 0)
                    {
                        Domain = us.UserName.Substring(0, us.UserName.LastIndexOf("\\"));
                        LoginName = us.UserName.Substring(us.UserName.LastIndexOf("\\") + 1, us.UserName.Length - us.UserName.LastIndexOf("\\") - 1);
                    }
                    else
                    {
                        Domain = Configuration.Instance.GetSystemParameter(Constants.PNDefaultDomainNBTName);
                        LoginName = us.UserName.Trim();
                    }
                    try
                    {
                        if (Domain.LastIndexOf(".") >= 0)
                        {
                            DirectoryEntry mDirEntry = new DirectoryEntry("LDAP://" + Domain, LoginName, us.Password, AuthenticationTypes.Secure);
                        }
                        else
                        {
                            DirectoryEntry mDirEntry = new DirectoryEntry("WinNT://" + Domain, LoginName, us.Password, AuthenticationTypes.Secure);
                        }
                    }
                    catch
                    {
                        throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Technical, Resources.UnableToValidateCredentials));

                    }
                    Context.MarkUserAuthenticated(LoginName, Domain);
                    return true;
                case "Kerberos":
                    if (context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated)
                    {
                        if (context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") > 0)
                        {
                            Domain = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(0, context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\"));
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") + 1, context.ServiceSecurityContext.WindowsIdentity.Name.Length - context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") - 1);
                        }
                        else
                        {
                            Domain = ParametersCache.GetVarchar(Constants.PNDefaultDomainNBTName);
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Trim();
                        }                        
                    }
                    return context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated;
                case "Negotiate":
                    if (context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated)
                    {
                        if (context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") > 0)
                        {
                            Domain = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(0, context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\"));
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Substring(context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") + 1, context.ServiceSecurityContext.WindowsIdentity.Name.Length - context.ServiceSecurityContext.WindowsIdentity.Name.LastIndexOf("\\") - 1);
                        }
                        else
                        {
                            Domain = ParametersCache.GetVarchar(Constants.PNDefaultDomainNBTName);
                            LoginName = context.ServiceSecurityContext.WindowsIdentity.Name.Trim();
                        }                        
                    }
                    return context.ServiceSecurityContext.WindowsIdentity.IsAuthenticated;
                case "X509":
                    throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Technical, "Not implemented yet"));
            }
            return false;
        }
	}
}
